/*****************************************************************************
 * Copyright (C) Stacy Curl. All rights reserved.                            *
 * ------------------------------------------------------------------------- *
 * The software in this package is published under the terms of the BSD      *
 * style license a copy of which has been included with this distribution in *
 * the LICENSE.txt file.                                                     *
 *****************************************************************************/
package picounit.features.signature_factory;


import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;

public class CollectionUtil {
	public static class ArrayIterable<T> implements Iterable<T> {
		private final T[] values;
		
		public ArrayIterable(T ... values) {
			this.values = values;
		}

		public Iterator<T> iterator() {
			return new ArrayIterator<T>(values);
		}
	}
	
	public static class ArrayIterator<T> implements Iterator<T> {
		private final T[] values;
		private int index = 0;

		public ArrayIterator(T ... values) {
			this.values = values;
		}
		
		public boolean hasNext() {
			return index < values.length;
		}

		public T next() {
			return values[index++];
		}

		public void remove() {
			throw new UnsupportedOperationException("Cannot remove an element from an array");
		}
	}
	
	public static class ReverseArrayIterator<T> implements Iterator<T> {
		private final T[] values;
		private int index;
	
		public ReverseArrayIterator(T ... values) {
			this.values = values;
			this.index = values.length - 1;
		}
	
		public boolean hasNext() {
			return index > 0;
		}
	
		public T next() {
			return values[index--];
		}
	
		public void remove() {
			throw new UnsupportedOperationException("Cannot remove an element from an array");
		}
	}

	public <From, To> Set<To> convert(Set<From> from, Converter<From, To> converter) {
		Set<To> to = new HashSet<To>(from.size());

		for (To toElement : iterable(from.iterator(), converter)) {
			to.add(toElement);
		}

		return to;
	}
	
	public <From, To> Iterator<To> iterate(Iterator<From> from, Converter<From, To> converter) {
		return new ConvertingIterator<From, To>(from, converter);
	}
	
	public <From, To> Iterable<To> iterable(final Iterator<From> from, final Converter<From, To> converter) {
		return new Iterable<To>() {
			public Iterator<To> iterator() {
				return iterate(from, converter);
			}
		};
	}

	public static Iterable<Object> reverse(final Object[] objects) {
		return new Iterable<Object>() {
			public Iterator<Object> iterator() {
				return new ReverseArrayIterator<Object>(objects);
			}
		};
	}

	public static Object[] nullToEmpty(Object[] array) {
		return array == null ? new Object[0] : array;
	}

	@SuppressWarnings("unchecked")
	public <F, T> T[] convert(F[] from, Converter<F,T> converter) {
		List<T> to = new ArrayList<T>(from.length);

		for (F f : from) {
			T convert = converter.convert(f);

			to.add(convert);
		}

		return to.toArray((T[]) Array.newInstance(converter.toClass(), from.length));
	}

	public static <T> Iterable<T> concat(Collection<T> ... collections) {
		return new ConcatenatingIterable<T>(collections);
	}
	
	public static class ConcatenatingIterable<T> implements Iterable<T> {
		private final class IteratorConverter extends AbstractConverter<Iterable<T>, Iterator<T>> {
			public IteratorConverter() {
				super(Iterator.class);
			}
			
			public Iterator<T> convert(Iterable<T> iterable) {
				return iterable.iterator();
			}
		}

		private Iterator<T>[] iterators;

		@SuppressWarnings("unchecked")
		public ConcatenatingIterable(Iterable<T> ... iterables) {
			this.iterators = new CollectionUtil().convert(iterables, new IteratorConverter()); 
		}

		public Iterator<T> iterator() {
			return new ConcatenatingIterator<T>(iterators);
		}
	}
	
	public static class ConcatenatingIterator<T> implements Iterator<T> {
		private final Iterator<Iterator<T>> iterators;
		private Iterator<T> current;

		public ConcatenatingIterator(Iterator<T> ... iterators) {
			this(new ArrayIterator<Iterator<T>>(iterators));
		}

		public ConcatenatingIterator(Iterator<Iterator<T>> iterators) {
			this.iterators = iterators;
			
			if (iterators.hasNext()) {
				this.current = iterators.next();
			}
		}

		public boolean hasNext() {
			if (current == null) {
				return false;
			}

			while (!current.hasNext()) {
				if (!iterators.hasNext()) {
					break;
				}

				current = iterators.next();
			}

			return current.hasNext();
		}

		public T next() {
			if (current == null) {
				throw new NoSuchElementException();
			}
			
			while (!current.hasNext()) {
				if (!iterators.hasNext()) {
					break;
				}
				
				current = iterators.next();
			}
			
			if (!current.hasNext()) {
				throw new NoSuchElementException();
			}

			return current.next();
		}

		public void remove() {
		}
	}
	
	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		Integer[] array = {1, 2, 3, 4, 5};
		List<Integer> list = new LinkedList<Integer>();
		list.add(6);
		list.add(7);
		list.add(8);
		list.add(9);
		list.add(10);
		
		for (Integer integer : new ConcatenatingIterable<Integer>(new ArrayIterable<Integer>(array), list)) {
			System.out.println(integer);
		}
	}
	
	public static <T> Concatenator<T> concatenate(Iterable<T> iterable) {
		return new Concatenator<T>(iterable);
	}
	
	public static class Concatenator<T> {
		private final List<Iterable<T>> iterables = new LinkedList<Iterable<T>>();
		
		public Concatenator(Iterable<T> iterable) {
			iterables.add(iterable);
		}
		
		
	}
}